package modules

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"
)

// UploadFile 上传文件
func UploadFile(w http.ResponseWriter, r *http.Request) {
	PrintRequestInfo(r)
	if err := r.ParseMultipartForm(32 << 20); err != nil {
		log.Printf("解析表单错误: %v", err)
		EncodeResponse(w, nil, "解析表单错误", http.StatusBadRequest)
		return
	}
	uploadDir := r.FormValue("dir")
	uploadDir, _ = url.QueryUnescape(uploadDir)
	if len(config.RootDirs) == 0 {
		log.Println("未配置根目录")
		EncodeResponse(w, nil, "未配置根目录", http.StatusBadRequest)
		return
	}
	parts := strings.SplitN(uploadDir, "/", 2)
	var rootName, subPath string
	if len(parts) == 1 {
		rootName = parts[0]
		subPath = ""
	} else {
		rootName = parts[0]
		subPath = parts[1]
	}
	if _, exists := rootNames[rootName]; !exists {
		log.Printf("指定的根目录名 %s 不存在", rootName)
		EncodeResponse(w, nil, "指定的目录不存在", http.StatusBadRequest)
		return
	}
	file, header, err := r.FormFile("file")
	if err != nil {
		log.Println("上传请求中未包含文件")
		EncodeResponse(w, nil, "上传请求中未包含文件", http.StatusBadRequest)
		return
	}
	defer file.Close()
	if header.Filename == "" {
		log.Println("未选择要上传的文件")
		EncodeResponse(w, nil, "未选择要上传的文件", http.StatusBadRequest)
		return
	}
	if !IsFileAllowed(header.Filename) {
		log.Printf("不支持的文件类型: %s", header.Filename)
		EncodeResponse(w, nil, "不支持的文件类型", http.StatusBadRequest)
		return
	}
	saveFilename := filepath.Join(rootNames[rootName], subPath, header.Filename)
	absSaveFilename, _ := filepath.Abs(saveFilename)

	if IsPathOutOfScope(saveFilename, rootName, w) {
		return
	}
	// 检查 absSaveFilename 文件所在的目录是否存在，如果不存在则创建
	if !CreateDirectory(filepath.Dir(absSaveFilename), w) {
		return
	}

	if _, err := os.Stat(absSaveFilename); !os.IsNotExist(err) {
		log.Println("文件已经存在")
		EncodeResponse(w, nil, "文件已经存在", http.StatusBadRequest)
		return
	}
	if !CreateDirectory(filepath.Dir(absSaveFilename), w) {
		return
	}
	outFile, err := os.Create(absSaveFilename)
	if err != nil {
		log.Printf("创建文件失败: %v", err)
		EncodeResponse(w, nil, "创建文件失败", http.StatusInternalServerError)
		return
	}
	defer outFile.Close()
	if _, err := io.Copy(outFile, file); err != nil {
		log.Printf("保存上传文件时出错: %v", err)
		EncodeResponse(w, nil, "保存文件失败", http.StatusInternalServerError)
		return
	}
	log.Printf("文件 %s 上传成功，保存路径: %s", header.Filename, saveFilename)
	EncodeResponse(w, map[string]string{
		"file_name": header.Filename,
	}, "", http.StatusOK)
}

// CheckUploadStatus 检查上传状态
func CheckUploadStatus(w http.ResponseWriter, r *http.Request) {
	PrintRequestInfo(r)
	if err := r.ParseForm(); err != nil {
		log.Printf("解析表单错误: %v", err)
		EncodeResponse(w, nil, "解析表单错误", http.StatusBadRequest)
		return
	}
	filename := r.FormValue("filename")
	filename, _ = url.QueryUnescape(filename)
	uploadDir := r.FormValue("dir")
	uploadDir, _ = url.QueryUnescape(uploadDir)
	if len(config.RootDirs) == 0 {
		log.Println("未配置根目录")
		EncodeResponse(w, nil, "未配置根目录", http.StatusBadRequest)
		return
	}

	if !IsFileAllowed(filename) {
		log.Printf("不支持的文件类型: %s", filename)
		EncodeResponse(w, nil, "不支持的文件类型", http.StatusBadRequest)
		return
	}

	parts := strings.SplitN(uploadDir, "/", 2)
	var rootName, subPath string
	if len(parts) == 1 {
		rootName = parts[0]
		subPath = ""
	} else {
		rootName = parts[0]
		subPath = parts[1]
	}
	if _, exists := rootNames[rootName]; !exists {
		log.Printf("指定的根目录名 %s 不存在", rootName)
		EncodeResponse(w, nil, "指定的目录不存在", http.StatusBadRequest)
		return
	}

	// 构建最终保存文件的路径
	savePath := filepath.Join(rootNames[rootName], subPath, filename)
	absSavePath, _ := filepath.Abs(savePath)
	if IsPathOutOfScope(savePath, rootName, w) {
		return
	}

	// 检查文件是否已经存在
	if _, err := os.Stat(absSavePath); !os.IsNotExist(err) {
		log.Println("文件已经存在")
		EncodeResponse(w, nil, "文件已经存在", http.StatusBadRequest)
		return
	}

	chunkDir := filepath.Join(rootNames[rootName], subPath, filename+"_chunks")
	absChunkDir, _ := filepath.Abs(chunkDir)
	if IsPathOutOfScope(chunkDir, rootName, w) {
		return
	}
	files, err := os.ReadDir(absChunkDir)
	if err != nil && !os.IsNotExist(err) {
		log.Printf("读取分片目录错误: %v", err)
		EncodeResponse(w, nil, "读取分片目录错误", http.StatusInternalServerError)
		return
	}
	var uploadedChunks []int = make([]int, 0)
	for _, file := range files {
		var chunkIndex int
		if _, err := fmt.Sscanf(file.Name(), "%d", &chunkIndex); err == nil {
			uploadedChunks = append(uploadedChunks, chunkIndex)
		}
	}
	sort.Ints(uploadedChunks)
	EncodeResponse(w, map[string]interface{}{
		"uploaded_chunks": uploadedChunks,
	}, "", http.StatusOK)
}

// UploadChunk 上传分片
func UploadChunk(w http.ResponseWriter, r *http.Request) {
	PrintRequestInfo(r)
	if err := r.ParseMultipartForm(32 << 20); err != nil {
		log.Printf("解析文件表单错误: %v", err)
		EncodeResponse(w, nil, "解析文件表单错误", http.StatusBadRequest)
		return
	}
	chunk, _, err := r.FormFile("chunk")
	if err != nil {
		log.Println("上传请求中未包含分片")
		EncodeResponse(w, nil, "上传请求中未包含分片", http.StatusBadRequest)
		return
	}
	defer chunk.Close()
	filename := r.FormValue("filename")
	filename, _ = url.QueryUnescape(filename)
	chunkIndexStr := r.FormValue("chunkIndex")
	totalChunksStr := r.FormValue("totalChunks")
	var chunkIndex, totalChunks int
	if _, err := fmt.Sscanf(chunkIndexStr, "%d", &chunkIndex); err != nil {
		log.Println("解析分片索引错误")
		EncodeResponse(w, nil, "解析分片索引错误", http.StatusBadRequest)
		return
	}
	if _, err := fmt.Sscanf(totalChunksStr, "%d", &totalChunks); err != nil {
		log.Println("解析总分片数错误")
		EncodeResponse(w, nil, "解析总分片数错误", http.StatusBadRequest)
		return
	}
	uploadDir := r.FormValue("dir")
	uploadDir, _ = url.QueryUnescape(uploadDir)
	if len(config.RootDirs) == 0 {
		log.Println("未配置根目录")
		EncodeResponse(w, nil, "未配置根目录", http.StatusBadRequest)
		return
	}
	parts := strings.SplitN(uploadDir, "/", 2)
	var rootName, subPath string
	if len(parts) == 1 {
		rootName = parts[0]
		subPath = ""
	} else {
		rootName = parts[0]
		subPath = parts[1]
	}
	if _, exists := rootNames[rootName]; !exists {
		log.Printf("指定的根目录名 %s 不存在", rootName)
		EncodeResponse(w, nil, "指定的目录不存在", http.StatusBadRequest)
		return
	}
	chunkDir := filepath.Join(rootNames[rootName], subPath, filename+"_chunks")
	absChunkDir, _ := filepath.Abs(chunkDir)
	if IsPathOutOfScope(absChunkDir, rootName, w) {
		return
	}
	if !CreateDirectory(absChunkDir, w) {
		return
	}
	chunkPath := filepath.Join(absChunkDir, fmt.Sprintf("%d", chunkIndex))
	outFile, err := os.Create(chunkPath)
	if err != nil {
		log.Printf("创建分片文件错误: %v", err)
		EncodeResponse(w, nil, "创建分片文件错误", http.StatusInternalServerError)
		return
	}
	defer outFile.Close()
	if _, err := io.Copy(outFile, chunk); err != nil {
		log.Printf("保存分片文件错误: %v", err)
		EncodeResponse(w, nil, "保存分片文件错误", http.StatusInternalServerError)
		return
	}
	log.Printf("文件 %s 的分片 %d/%d 上传成功", filename, chunkIndex+1, totalChunks)
	EncodeResponse(w, nil, "", http.StatusOK)
}

// MergeChunks 合并分片
func MergeChunks(w http.ResponseWriter, r *http.Request) {
	PrintRequestInfo(r)
	if err := r.ParseForm(); err != nil {
		log.Printf("解析表单错误: %v", err)
		EncodeResponse(w, nil, "解析表单错误", http.StatusBadRequest)
		return
	}
	filename := r.FormValue("filename")
	filename, _ = url.QueryUnescape(filename)
	totalChunksStr := r.FormValue("totalChunks")
	uploadDir := r.FormValue("dir")
	uploadDir, _ = url.QueryUnescape(uploadDir)
	var totalChunks int
	if _, err := fmt.Sscanf(totalChunksStr, "%d", &totalChunks); err != nil {
		log.Println("解析总分片数错误")
		EncodeResponse(w, nil, "解析总分片数错误", http.StatusBadRequest)
		return
	}
	if len(config.RootDirs) == 0 {
		log.Println("未配置根目录")
		EncodeResponse(w, nil, "未配置根目录", http.StatusBadRequest)
		return
	}
	parts := strings.SplitN(uploadDir, "/", 2)
	var rootName, subPath string
	if len(parts) == 1 {
		rootName = parts[0]
		subPath = ""
	} else {
		rootName = parts[0]
		subPath = parts[1]
	}
	if _, exists := rootNames[rootName]; !exists {
		log.Printf("指定的根目录名 %s 不存在", rootName)
		EncodeResponse(w, nil, "指定的目录不存在", http.StatusBadRequest)
		return
	}
	chunkDir := filepath.Join(rootNames[rootName], subPath, filename+"_chunks")
	savePath := filepath.Join(rootNames[rootName], subPath, filename)
	absSavePath, _ := filepath.Abs(savePath)
	if IsPathOutOfScope(savePath, rootName, w) {
		return
	}
	if _, err := os.Stat(absSavePath); !os.IsNotExist(err) {
		log.Println("文件已经存在")
		EncodeResponse(w, nil, "文件已经存在", http.StatusBadRequest)
		return
	}
	outFile, err := os.Create(absSavePath)
	if err != nil {
		log.Printf("创建合并文件错误: %v", err)
		EncodeResponse(w, nil, "创建合并文件错误", http.StatusInternalServerError)
		return
	}
	defer outFile.Close()
	for i := 0; i < totalChunks; i++ {
		chunkPath := filepath.Join(chunkDir, fmt.Sprintf("%d", i))
		if _, err := os.Stat(chunkPath); os.IsNotExist(err) {
			log.Printf("分片 %d 丢失，合并失败", i)
			EncodeResponse(w, nil, "分片丢失，合并失败", http.StatusInternalServerError)
			return
		}
		chunkFile, err := os.Open(chunkPath)
		if err != nil {
			log.Printf("打开分片文件 %d 错误: %v", i, err)
			EncodeResponse(w, nil, "打开分片文件错误", http.StatusInternalServerError)
			return
		}
		if _, err := io.Copy(outFile, chunkFile); err != nil {
			chunkFile.Close()
			log.Printf("合并分片文件 %d 错误: %v", i, err)
			EncodeResponse(w, nil, "合并分片文件错误", http.StatusInternalServerError)
			return
		}
		// 及时将数据写入磁盘
		outFile.Sync()
		chunkFile.Close()
	}
	log.Printf("文件 %s 合并成功，保存路径: %s", filename, savePath)
	// 等待 100ms 再删除，避免文件柄未关闭完成
	time.Sleep(100 * time.Millisecond)
	if err := os.RemoveAll(chunkDir); err != nil {
		log.Printf("删除分片目录错误: %v", err)
	}
	EncodeResponse(w, map[string]string{
		"file_name": filename,
	}, "", http.StatusOK)
}
